#include "TileRenderMgr.h"
#include "CachedTexture.h"



TileRenderMgr::TileRenderMgr() {

	VS_Tile = NULL;
	HS_Tile = NULL;
	DS_Tile = NULL;
	PS_Tile = NULL;




	bShowWireFrame = false;
}

TileRenderMgr::~TileRenderMgr() 
{
	for(auto it = SM.begin(); it != SM.end(); it++)
	{
		delete [] it->Name;
		delete [] it->Definition;
	}
	IL_TILEVERTEX_HIGH.Release();
	/*REL( VS_Tile );
	REL( HS_Tile );
	REL( DS_Tile );
	REL( PS_Tile );*/
	REL( DisplacementMicroTex );
	REL( aTerrainMicroTex );
}

void TileRenderMgr::UpdatePerFrameBuffers( float dist_scale, float coef_altitude, float luminance ) {
	

}

//=========================================================
//			L1-L4
//=========================================================

void TileRenderMgr::RenderSphere(UINT8 level, UINT npatch, TILEDESC1 *td, D3DXMATRIX *mWorld, float dist_scale, float luminance) {
	const UINT VBOffset = 0;

	UpdatePerFrameBuffers(dist_scale, 0.0f, luminance);

	iCtx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	iCtx->IASetInputLayout(IL_TILEVERTEX_LOW);

	iCtx->VSSetShader(VS_Tile_Far[0], NULL, 0);//!
	iCtx->VSSetConstantBuffers(0, 2, VSCB_Tile);

	iCtx->PSSetShader(PS_Tile, NULL, 0);
	iCtx->PSSetConstantBuffers(0, 2, PSCB_Tile);
	iCtx->PSSetConstantBuffers(2, 1, &CB_AtmParams);

	iCtx->PSSetSamplers(0, 1, &SS_TileTexture);
	iCtx->PSSetSamplers(1, 1, &SS_TileTexture);

	iCtx->OMSetBlendState(BS_NoBlend, D3DXVECTOR4(1, 1, 1, 1), 0xFFFFFFFF);
	iCtx->OMSetDepthStencilState(DSS_NoDepth_NoStencil, 0);
	iCtx->RSSetState(RS_CullBack_Solid);

	PSCB_per_tile pbuf;
	pbuf.color_mult = 1.0f;

}

//=========================================================
//			L5-L7
//=========================================================

void TileRenderMgr::RenderLowResTiles( TILERENDERSPEC *TRS, UINT ntile, float dist_scale, float luminance ) {
	const UINT VBOffset = 0;

	UpdatePerFrameBuffers( dist_scale, 0.0f, luminance );

	iCtx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
	iCtx->IASetInputLayout( IL_TILEVERTEX_LOW );
	
	iCtx->VSSetShader( VS_Tile_Far[0], NULL, 0 );
	iCtx->VSSetConstantBuffers( 0, 2, VSCB_Tile );	

	iCtx->PSSetShader( PS_Tile, NULL, 0 );
	iCtx->PSSetConstantBuffers( 0, 2, PSCB_Tile );
	iCtx->PSSetConstantBuffers( 2, 1, &CB_AtmParams );

	iCtx->PSSetSamplers( 0, 1, &SS_TileTexture );
	iCtx->PSSetSamplers( 1, 1, &SS_MicroTexture );

	iCtx->OMSetBlendState( BS_NoBlend, D3DXVECTOR4( 1, 1, 1, 1 ), 0xFFFFFFFF );
	iCtx->OMSetDepthStencilState( DSS_NoDepth_NoStencil, 0 );
	iCtx->RSSetState( RS_CullBack_Solid );

	VSCB_per_tile vbuf;
	PSCB_per_tile pbuf;
	pbuf.color_mult = 1.0f;

}

//=========================================================
//			L8-L18
//=========================================================

void TileRenderMgr::RenderHighResTiles( TILERENDERSPEC *TRS, UINT ntile, float dist_scale, float coef_altitude, float luminance ) {
	UINT j;
	const UINT VBOffset = 0;
	
	UpdatePerFrameBuffers( dist_scale, coef_altitude, luminance );

	iCtx->IASetInputLayout( IL_TILEVERTEX_HIGH );
	iCtx->IASetVertexBuffers( 0, 1, &VB_TPL, &Stride_TILEVERTEX_HIGH, &VBOffset );
	iCtx->IASetIndexBuffer( IB, DXGI_FORMAT_R16_UINT, 0 );
	iCtx->RSSetState( RS_CullBack_Solid );
	
	if( FL == 10 ) {
		iCtx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
		iCtx->VSSetShader( VS_Tile, NULL, 0 );
		iCtx->VSSetSamplers( 0, 1, &SS_HeightMap );
		iCtx->VSSetConstantBuffers( 0, 2, VSCB_Tile );		
	}
	else {
		iCtx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST );
		iCtx->VSSetShader( VS_Tile, NULL, 0 );
		iCtx->VSSetConstantBuffers( 0, 2, VSCB_Tile );

		iCtx->HSSetShader( HS_Tile, NULL, 0 );
		iCtx->HSSetConstantBuffers( 0, 1, &HSCB_Tile );
		iCtx->HSSetSamplers( 0, 1, &SS_HeightMap );
		if( DisplacementMicroTex ) {
			iCtx->HSSetSamplers( 1, 1, &SS_MicroTexture );
			iCtx->HSSetShaderResources( 1, 1, &DisplacementMicroTex );
		}

		iCtx->DSSetShader( DS_Tile, NULL, 0 );
		iCtx->DSSetConstantBuffers( 0, 2, VSCB_Tile );
	
		iCtx->DSSetSamplers( 0, 1, &SS_HeightMap );
		if( DisplacementMicroTex ) {
			iCtx->DSSetSamplers( 1, 1, &SS_MicroTexture );
			iCtx->DSSetShaderResources( 1, 1, &DisplacementMicroTex );
		}
	}

	iCtx->PSSetShader( PS_Tile, NULL, 0 );
	iCtx->PSSetConstantBuffers( 0, 2, PSCB_Tile );
	iCtx->PSSetConstantBuffers( 2, 1, &CB_AtmParams );
	
	iCtx->PSSetSamplers( 0, 1, &SS_TileTexture );
	iCtx->PSSetSamplers( 1, 1, &SS_MicroTexture );

	if( WaterMicroTex )		iCtx->PSSetShaderResources( 4, 1, &WaterMicroTex );
	if( aTerrainMicroTex )	iCtx->PSSetShaderResources( 5, 1, &aTerrainMicroTex );
//	if( rmgr && CFG->bRingShadows )	iCtx->PSSetShaderResources( 7, 1, &rmgr->tex );//texture !

	iCtx->OMSetBlendState( BS_NoBlend, D3DXVECTOR4( 1, 1, 1, 1 ), 0xFFFFFFFF );
	iCtx->OMSetDepthStencilState( DSS_Tile, 0 );
	
}

void AddShaderMacro(std::vector<D3D10_SHADER_MACRO>& sm, const D3D10_SHADER_MACRO& macro)
{
	D3D10_SHADER_MACRO newMacro;
	auto str = new char[strlen(macro.Name) + 1];
	strcpy(str, macro.Name);
	newMacro.Name = str;
	str = new char[strlen(macro.Definition) + 1];
	strcpy(str, macro.Definition);
	newMacro.Definition = str;
	sm.push_back(newMacro);
}

float TileRenderMgr::InitResources( float **flat_disp_data, float **coarse_disp_data, D3D11Client * client ) {
	
	return 0.0;
}

bool TileRenderMgr::AddTexture( std::vector<BYTE*> &TEX, BYTE *data, UINT *twidth, UINT *theight, UINT &nmip ) {
	D3D11_SUBRESOURCE_DATA sdata;

	//check dimesions
	if( twidth[0] != twidth[1] || theight[0] != theight[1] ) {
		if( !twidth[1] && !theight[1] ) {
			//first array element
			twidth[1] = twidth[0];
			theight[1] = theight[0];
		}
		else {
			oapiWriteLog( "Error: dimensions of microtextures must be equal to each other." );
			for( int j = 0; j < TEX.size(); j++ )	delete [ ] TEX[j];
			TEX.clear();
			return false;
		}
	}

	TEX.push_back( data );
	if( !nmip )		GetMipMapNumber( twidth[0], theight[0], nmip );
	return true;
}

bool ReadTexture( BYTE **data, char *fname, UINT &twidth, UINT &theight ) {

	FILE *file;
	if( !fname || !(file = fopen( fname, "rb" )) )	return 0;

	DWORD key;
	fread( &key, 4, 1, file );
	if( key != MAKEFOURCC( 'D', 'D', 'S', ' ' ) ) {
		fclose( file );
		return false;
	}

	DDS_HEADER HDR;
	fread( &HDR, sizeof(HDR), 1, file );
	twidth = HDR.dwWidth;
	theight = HDR.dwHeight;

	*data = new BYTE [HDR.dwPitchOrLinearSize];
	fread( *data, 1, HDR.dwPitchOrLinearSize, file );
	fclose( file );
	return true;
}

bool GetMipMapNumber( UINT w, UINT h, UINT &nmip ) {
	UINT maxd = max( w, h );
	nmip = 0;
	for( float d = (float)maxd; d > 0.98f; d /= 2.0f )
		nmip++;
	return true;
}

//============================================================================
//	
//============================================================================

TILEVERTEX_LOW _TVTX_LOW( D3DXVECTOR3 pos, D3DXVECTOR3 norm, float tu, float tv );

UINT
	TileRenderMgr::FL = 10;

CComPtrEx<ID3D11InputLayout> TileRenderMgr::IL_TILEVERTEX_HIGH;
ID3D11InputLayout
	*TileRenderMgr::IL_TILEVERTEX_LOW	= NULL;
	//*TileRenderMgr::IL_TILEVERTEX_HIGH	= NULL;
ID3D11VertexShader
	*TileRenderMgr::VS_Tile_Far[2]		= { NULL };
ID3D11SamplerState
	*TileRenderMgr::SS_HeightMap		= NULL,
	*TileRenderMgr::SS_TileTexture		= NULL,
	*TileRenderMgr::SS_MicroTexture		= NULL;
ID3D11Buffer
	*TileRenderMgr::VB_TPL				= NULL,
	*TileRenderMgr::IB					= NULL,
	*TileRenderMgr::CB_AtmParams		= NULL,
	*TileRenderMgr::CB_PlanetShadowParams	= NULL,
	*TileRenderMgr::CB_RingParams		= NULL,
	*TileRenderMgr::VSCB_Tile[2]		= { NULL },
	*TileRenderMgr::PSCB_Tile[2]		= { NULL },
	*TileRenderMgr::HSCB_Tile			= NULL;
ID3D11ShaderResourceView
	*TileRenderMgr::WaterMicroTex		= NULL;
ID3D11DepthStencilState
	*TileRenderMgr::DSS_Tile			= NULL;
TILEMESH1
	TileRenderMgr::TPL_1,
	TileRenderMgr::TPL_2,
	TileRenderMgr::TPL_3,
	TileRenderMgr::TPL_4[2],
	TileRenderMgr::TPL_5,
	TileRenderMgr::TPL_6[2],
	TileRenderMgr::TPL_7[4];
TileRenderMgr::TILE_RES_LVL
	TileRenderMgr::TRES[3];
UINT
	TileRenderMgr::Stride_TILEVERTEX_LOW = 44,
	TileRenderMgr::Stride_TILEVERTEX_HIGH = 16;
TILEMESH1 *TileRenderMgr::TPL[8] = { NULL, &TPL_1, &TPL_2, &TPL_3, TPL_4, &TPL_5, TPL_6, TPL_7 };
UINT TileRenderMgr::patchidx[9] = { 0, 1, 2, 3, 5, 13, 37, 137, 501 };
UINT TileRenderMgr::NLAT[9] = { 0, 1, 1, 1, 1, 1, 2, 4, 8 };
UINT
	TileRenderMgr::NLNG5[1] = { 4 },
	TileRenderMgr::NLNG6[2] = { 8, 4 },
	TileRenderMgr::NLNG7[4] = { 16, 16, 12, 6 },
	TileRenderMgr::NLNG8[8] = { 32, 32, 30, 28, 24, 18, 12, 6 }, 
	*TileRenderMgr::NLNG[9] = { NULL, NULL, NULL, NULL, NULL, NLNG5, NLNG6, NLNG7, NLNG8 };

void TileRenderMgr::GlobalInit( ID3D11Buffer **_CB_AtmParams, ID3D11Buffer **_CB_PlanetShadowParams ) {
	DWORD j;
	HRESULT hr;
	ID3DBlob *SBlob = NULL, *EBlob = NULL;

	FL = gd3dDevice->GetFeatureLevel() == D3D_FEATURE_LEVEL_11_0 ? 11 : 10;

	Stride_TILEVERTEX_LOW = 44;
	Stride_TILEVERTEX_HIGH = 20;

	//===================================================
	//				Basic Shaders
	//===================================================

	D3D10_SHADER_MACRO macro;
	std::vector<D3D10_SHADER_MACRO> mlist;

	if( FL == 11 ) {
		macro.Name = "FL11";
		macro.Definition = "1";
		mlist.push_back(  macro );
	}

	UINT dim, nvtx, nidx;
	memset( &TRES, 0, sizeof(TILE_RES_LVL)*3 );
	//================================================================================
	//						Tiles without heightmaps
	//			flat terrain or altitude above normal/height map transition
	//================================================================================		
	dim = 12;
	TRES[0].vdim = dim;
	TRES[0].edge_tf[0] = 1.0f;
	TRES[0].edge_tf[1] = 1.0f;
	TRES[0].inside_tf[0] = 1.0f;
	TRES[0].inside_tf[1] = 1.0f;

	TRES[0].svtx[0] = 0;
	TRES[0].nvtx[0] = (dim+1)*(dim+1);
	TRES[0].sidx[0] = 0;
	TRES[0].nidx[0] = 6*dim*dim;

	TRES[0].svtx[1] = TRES[0].nvtx[0];
	TRES[0].nvtx[1] = (dim+1)*(dim+1) - ((dim+1)*dim)/2;
	TRES[0].sidx[1] = TRES[0].nidx[0];
	TRES[0].nidx[1] = 3*dim*dim;

	//================================================================================
	//					Tiles with non-rough heightmaps (water, valleys)
	//			flat terrain or altitude above normal/height map transition
	//================================================================================
	dim = (FL == 11 ? 16 : 34);
	TRES[1].vdim = dim;
	TRES[1].edge_tf[0] = 1.0f;
	TRES[1].edge_tf[1] = 3.0f;
	TRES[1].inside_tf[0] = 1.0f;
	TRES[1].inside_tf[1] = 3.0f;

	TRES[1].svtx[0] = TRES[0].svtx[1] + TRES[0].nvtx[1];
	TRES[1].nvtx[0] = (dim+1)*(dim+1);
	TRES[1].sidx[0] = TRES[0].sidx[1] + TRES[0].nidx[1];
	TRES[1].nidx[0] = 6*dim*dim;

	TRES[1].svtx[1] = TRES[1].svtx[0] + TRES[1].nvtx[0];
	TRES[1].nvtx[1] = (dim+1)*(dim+1) - ((dim+1)*dim)/2;
	TRES[1].sidx[1] = TRES[1].sidx[0] + TRES[1].nidx[0];
	TRES[1].nidx[1] = 3*dim*dim;

	//================================================================================
	//					Tiles with coarse heightmaps (mountains)
	//			flat terrain or altitude above normal/height map transition
	//================================================================================
	dim = (FL == 11 ? 16 : 66);
	TRES[2].vdim = dim;
	TRES[2].edge_tf[0] = 3.0f;
	TRES[2].edge_tf[1] = 5.0f;
	TRES[2].inside_tf[0] = 1.0f;
	TRES[2].inside_tf[1] = 4.0f;

	TRES[2].svtx[0] = TRES[1].svtx[1] + TRES[1].nvtx[1];
	TRES[2].nvtx[0] = (dim+1)*(dim+1);
	TRES[2].sidx[0] = TRES[1].sidx[1] + TRES[1].nidx[1];
	TRES[2].nidx[0] = 6*dim*dim;

	TRES[2].svtx[1] = TRES[2].svtx[0] + TRES[2].nvtx[0];
	TRES[2].nvtx[1] = (dim+1)*(dim+1) - ((dim+1)*dim)/2;
	TRES[2].sidx[1] = TRES[2].sidx[0] + TRES[2].nidx[0];
	TRES[2].nidx[1] = 3*dim*dim;

	nvtx = TRES[2].svtx[1] + TRES[2].nvtx[1];
	nidx = TRES[2].sidx[1] + TRES[2].nidx[1];
	//================================================================================
	//================================================================================

	macro.Name = "DISPLACEMENT_HEIGHT_DIM_RATIO";
	macro.Definition = "1.0f";
	mlist.push_back( macro );

	macro.Name = "DISPLACEMENT_MICROTEX_PSTRIDE";
	macro.Definition = "1.0f";
	mlist.push_back( macro );

	macro.Name = "MIN_ANGLE";
	macro.Definition = "0.0f";
	mlist.push_back( macro );

	macro.Name = "MAX_ANGLE";
	macro.Definition = "1.0f";
	mlist.push_back( macro );

	macro.Name = "HEIGHT_SCALE";
	macro.Definition = "1";
	mlist.push_back( macro );

	macro.Name = NULL;
	macro.Definition = NULL;
	mlist.push_back( macro );

	D3D11_INPUT_ELEMENT_DESC iedesc_low[ ] = {
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 36, D3D11_INPUT_PER_VERTEX_DATA, 0 }
	};

	::CompileFromFile( "Modules\\D3D11Shaders\\Planet\\VS_Planet1.fx", &mlist[0], NULL, "VS_Tile_Far", vs_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
	ShowShaderCompilationError( hr, EBlob );
	HR( gd3dDevice->CreateInputLayout( iedesc_low, 4, SBlob->GetBufferPointer(), SBlob->GetBufferSize(), &IL_TILEVERTEX_LOW ) );
	HR( gd3dDevice->CreateVertexShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &VS_Tile_Far[0] ) );
	REL( SBlob );
	REL( EBlob );

	mlist[mlist.size()-1].Name = "BASIC_ATMOSPHERE";
	mlist[mlist.size()-1].Definition = "1";
	macro.Name = NULL;
	macro.Definition = NULL;
	mlist.push_back( macro );

	::CompileFromFile( "Modules\\D3D11Shaders\\Planet\\VS_Planet1.fx", &mlist[0], NULL, "VS_Tile_Far", vs_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
	ShowShaderCompilationError( hr, EBlob );
	HR( gd3dDevice->CreateVertexShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &VS_Tile_Far[1] ) );
	REL( SBlob );
	REL( EBlob );

	//===================================================
	//				Constant buffers
	//===================================================

	D3D11_BUFFER_DESC bdesc;

	memset( &bdesc, 0, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	bdesc.ByteWidth = 80;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_DEFAULT;

	HR( gd3dDevice->CreateBuffer( &bdesc, NULL, &VSCB_Tile[0] ) );

	memset( &bdesc, 0, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	bdesc.ByteWidth = 160;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_DEFAULT;

	HR( gd3dDevice->CreateBuffer( &bdesc, NULL, &VSCB_Tile[1] ) );

	memset( &bdesc, 0, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	bdesc.ByteWidth = 64;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_DEFAULT;

	HR( gd3dDevice->CreateBuffer( &bdesc, NULL, _CB_AtmParams ) );//_CB_AtmParams is in TerrainManager
	CB_AtmParams = *_CB_AtmParams;

	memset( &bdesc, 0, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	bdesc.ByteWidth = 48;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_DEFAULT;

	HR( gd3dDevice->CreateBuffer( &bdesc, NULL, _CB_PlanetShadowParams ) );//_CB_AtmParams is in TerrainManager
	CB_PlanetShadowParams = *_CB_PlanetShadowParams;

	if( FL == 11 ) {
		memset( &bdesc, 0, sizeof(bdesc) );
		bdesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
		bdesc.ByteWidth = 16;
		bdesc.CPUAccessFlags = 0;
		bdesc.MiscFlags = 0;
		bdesc.StructureByteStride = 0;
		bdesc.Usage = D3D11_USAGE_DEFAULT;

		HR( gd3dDevice->CreateBuffer( &bdesc, NULL, &HSCB_Tile ) );
	}

	memset( &bdesc, 0, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	bdesc.ByteWidth = 32;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_DEFAULT;

	HR( gd3dDevice->CreateBuffer( &bdesc, NULL, &PSCB_Tile[0] ) );

	memset( &bdesc, 0, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	bdesc.ByteWidth = 16;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_DEFAULT;

	HR( gd3dDevice->CreateBuffer( &bdesc, NULL, &PSCB_Tile[1] ) );

	//===================================================
	//				Texture Samplers
	//===================================================

	D3D11_SAMPLER_DESC sdesc;

	memset( &sdesc, 0, sizeof(sdesc) );
	sdesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
	sdesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
	sdesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
	sdesc.Filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;//<= cfg
	sdesc.ComparisonFunc = D3D11_COMPARISON_EQUAL;
	sdesc.MaxAnisotropy = 1;
	sdesc.MipLODBias = 0;
	sdesc.MinLOD = 0;
	sdesc.MaxLOD = D3D11_FLOAT32_MAX;

	gd3dDevice->CreateSamplerState( &sdesc, &SS_HeightMap );

	memset( &sdesc, 0, sizeof(sdesc) );
	sdesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
	sdesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
	sdesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
	sdesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;//<= cfg
	sdesc.ComparisonFunc = D3D11_COMPARISON_EQUAL;
	sdesc.MaxAnisotropy = 1;
	sdesc.MipLODBias = 0;
	sdesc.MinLOD = 0;
	sdesc.MaxLOD = D3D11_FLOAT32_MAX;

	HR( gd3dDevice->CreateSamplerState( &sdesc, &SS_TileTexture ) );

	//water and forest microtextures
	memset( &sdesc, 0, sizeof(sdesc) );
	sdesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
	sdesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
	sdesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
	sdesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;//<= cfg
	sdesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
	sdesc.MaxAnisotropy = 1;
	sdesc.MipLODBias = 0;
	sdesc.MinLOD = 0;
	sdesc.MaxLOD = D3D11_FLOAT32_MAX;

	HR( gd3dDevice->CreateSamplerState( &sdesc, &SS_MicroTexture) );

	//===================================================
	//				ShadowVolume texture
	//===================================================

	ID3D11Texture2D *tex;
	D3D11_TEXTURE2D_DESC tdesc;
	D3D11_RENDER_TARGET_VIEW_DESC rtvdesc;
	D3D11_SHADER_RESOURCE_VIEW_DESC srvdesc;

/*	memset( &tdesc, 0, sizeof(tdesc) );
	tdesc.ArraySize = 1;
	tdesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
	tdesc.CPUAccessFlags = 0;
	tdesc.Format = DXGI_FORMAT_R32_FLOAT;
	tdesc.Height = cfg->cHeight;
	tdesc.MipLevels = 1;
	tdesc.MiscFlags = 0;
	tdesc.SampleDesc.Count = 1;
	tdesc.Usage = D3D11_USAGE_DEFAULT;
	tdesc.Width = cfg->cWidth;

	HR( gd3dDevice->CreateTexture2D( &tdesc, NULL, &tex ) );

	memset( &rtvdesc, 0, sizeof(rtvdesc) );
	rtvdesc.Format = DXGI_FORMAT_R32_FLOAT;
	rtvdesc.Texture2D.MipSlice = 0;
	rtvdesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;

	gd3dDevice->CreateRenderTargetView( tex, &rtvdesc, &ShadowVolumeRTV );

	memset( &srvdesc, 0, sizeof(srvdesc) );
	srvdesc.Format = DXGI_FORMAT_R32_FLOAT;
	srvdesc.Texture2D.MipLevels = 1;
	srvdesc.Texture2D.MostDetailedMip = 0;
	srvdesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;

	gd3dDevice->CreateShaderResourceView( tex, &srvdesc, &ShadowVolumeSRV );

	REL( tex );*/
	
	//===================================================
	//				DepthStencilStates
	//===================================================

	D3D11_DEPTH_STENCIL_DESC ddesc;

	memset( &ddesc, 0, sizeof(ddesc) );
	ddesc.DepthEnable = true;
	ddesc.DepthFunc = D3D11_COMPARISON_LESS_EQUAL;
	ddesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
	ddesc.StencilEnable = false;
	ddesc.StencilReadMask = 0;
	ddesc.StencilWriteMask = 0;

	HR( gd3dDevice->CreateDepthStencilState( &ddesc, &DSS_Tile ) );
		
	//===================================================
	//				Build meshes
	//===================================================

	CreateSphere( TPL_1, 6, false, 0, 64 );
	CreateSphere( TPL_2, 8, false, 0, 128 );
	CreateSphere( TPL_3, 12, false, 0, 256 );
	CreateSphere( TPL_4[0], 16, true, 0, 256 );
	CreateSphere( TPL_4[1], 16, true, 1, 256 );

	CreateSpherePatch_LOW( TPL_5, 0, 1, 4, 18 );
	CreateSpherePatch_LOW( TPL_6[0], 0, 2, 8, 10, 16 );
	CreateSpherePatch_LOW( TPL_6[1], 1, 2, 4, 12 );
	CreateSpherePatch_LOW( TPL_7[0], 0, 4, 16, 12, 12, false );
	CreateSpherePatch_LOW( TPL_7[1], 1, 4, 16, 12, 12, false );
	CreateSpherePatch_LOW( TPL_7[2], 2, 4, 12, 10, 16, true );
	CreateSpherePatch_LOW( TPL_7[3], 3, 4, 6, 12, 0xFFFFFFFF, true );

	//build L8 meshes for L > L8	
	TILEVERTEX_HIGH *Vtx = new TILEVERTEX_HIGH [nvtx], *vtx;
	UINT16 *Idx = new UINT16 [nidx], *idx;

	vtx = Vtx;
	idx = Idx;

	for( int j = 0; j < 3; j++ ) {
		CreateSpherePatch_HIGH( vtx, idx, 0, TRES[j].vdim, TRES[j].vdim );
		vtx += TRES[j].nvtx[0];
		idx += TRES[j].nidx[0];

		CreateSpherePatch_HIGH( vtx, idx, 7, TRES[j].vdim );
		vtx += TRES[j].nvtx[1];
		idx += TRES[j].nidx[1];
	}
	
	D3D11_SUBRESOURCE_DATA sdata;

	memset( &bdesc, 0, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bdesc.ByteWidth = nvtx*Stride_TILEVERTEX_HIGH;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_IMMUTABLE;

	memset( &sdata, 0, sizeof(sdata) );
	sdata.pSysMem = Vtx;

	HR( gd3dDevice->CreateBuffer( &bdesc, &sdata, &VB_TPL ) );

	memset( &bdesc, 0, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
	bdesc.ByteWidth = nidx*2;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_IMMUTABLE;

	memset( &sdata, 0, sizeof(sdata) );
	sdata.pSysMem = Idx;

	HR( gd3dDevice->CreateBuffer( &bdesc, &sdata, &IB ) );

	delete [ ] Vtx;
	delete [ ] Idx;

	InitTerrainShadows();
}

void TileRenderMgr::GlobalExit() {
	void DeleteTILEMESH1( TILEMESH1 &mesh );

	DeleteTILEMESH1( TPL_1 );
	DeleteTILEMESH1( TPL_2 );
	DeleteTILEMESH1( TPL_3 );
	DeleteTILEMESH1( TPL_4[0] );
	DeleteTILEMESH1( TPL_4[1] );
	DeleteTILEMESH1( TPL_5 );
	DeleteTILEMESH1( TPL_6[0] );
	DeleteTILEMESH1( TPL_6[1] );
	DeleteTILEMESH1( TPL_7[0] );
	DeleteTILEMESH1( TPL_7[1] );
	DeleteTILEMESH1( TPL_7[2] );
	DeleteTILEMESH1( TPL_7[3] );

	REL( VB_TPL );
	REL( IB );
	REL( CB_AtmParams );
	REL( CB_PlanetShadowParams );
	REL( CB_RingParams );
	REL( VSCB_Tile[0] );
	REL( VSCB_Tile[1] );
	REL( HSCB_Tile );
	REL( PSCB_Tile[0] );
	REL( PSCB_Tile[1] );

	REL( WaterMicroTex );
	REL( SS_HeightMap );
	REL( SS_TileTexture );
	REL( SS_MicroTexture );
	
	REL( IL_TILEVERTEX_LOW );
	IL_TILEVERTEX_HIGH.Release();
	REL( VS_Tile_Far[0] );
	REL( VS_Tile_Far[1] );

	REL( DSS_Tile );

	ExitTerrainShadows();
}

void DeleteTILEMESH1( TILEMESH1 &mesh ) {
	REL( mesh.VB );
	REL( mesh.IB );
}

void TileRenderMgr::CreateSphere( TILEMESH1 &mesh, UINT nrings, bool hemisphere, int which_half, int texres ) {
	//creates full sphere mesh for L1-L4

	mesh.nvtx = hemisphere ? nrings*(nrings+1)+2 : nrings*(2*nrings+1)+2;
	mesh.nidx = hemisphere ? 6*nrings*nrings : 12*nrings*nrings;

	TILEVERTEX_LOW *Vtx, *vtx;
	Vtx = new TILEVERTEX_LOW [mesh.nvtx];
	vtx = Vtx;

	WORD *Idx, *idx;
	Idx = new WORD [mesh.nidx];
	idx = Idx;

	WORD x, y, nvtx = 0, nidx = 0;
	double fDAng = PI/nrings;
	double fDAngY0 = fDAng;

	DWORD x1 = ( hemisphere ? nrings : nrings*2 );
	DWORD x2 = x1 + 1;

	double du = 0.5f/(double)texres;
	double a = (1.0f-2.0f*du)/(double)x1;
	double y0, r0, tv, fDAngX0, tu;
	D3DXVECTOR3 vec;

	//positions, normals and texcoords
	for( y = 0; y < nrings; y++ ) {
		y0 = cos(fDAngY0);
		r0 = sin(fDAngY0);
		tv = fDAngY0/PI;
		for( x = 0; x < x2; x++ ) {
			fDAngX0 = x*fDAng - PI;
			if( hemisphere && which_half )
				fDAngX0 += PI;
			vec = D3DXVECTOR3( float(r0*cos(fDAngX0)), float(y0), float(r0*sin(fDAngX0)) );
			tu = float(a*(double)x + du);
			*vtx = _TVTX_LOW( vec*1000.0f, vec, tu, tv );
			vtx->TangL = D3DXVECTOR3( float(-1.0*sin(fDAngX0)), 0.0f, float(cos(fDAngX0)) );
			vtx++;
			nvtx++;
		}
		fDAngY0 += fDAng;
	}

	for( y = 0; y < nrings-1; y++ ) {
		for( x = 0; x < x1; x++ ) {
			*idx++ = (WORD)( (y+0)*x2 + (x+0) );
			*idx++ = (WORD)( (y+0)*x2 + (x+1) );
			*idx++ = (WORD)( (y+1)*x2 + (x+0) );
			*idx++ = (WORD)( (y+0)*x2 + (x+1) );
			*idx++ = (WORD)( (y+1)*x2 + (x+1) );
			*idx++ = (WORD)( (y+1)*x2 + (x+0) );
			nidx += 6;
		}
	}

	vec = D3DXVECTOR3( 0.0f, 1.0f, 0.0f );
	WORD wNorthVtx = nvtx;

	*vtx++ = _TVTX_LOW( 1000.0f*vec, vec, 0.5f, 0.0f );
	nvtx++;

	vec = D3DXVECTOR3( 0.0f, -1.0f, 0.0f );
	WORD wSouthVtx = nvtx;
	*vtx++ = _TVTX_LOW( 1000.0f*vec, vec, 0.5f, 1.0f );
	nvtx++;

	for( x = 0; x < x1; x++ ) {
		*idx++ = wSouthVtx;
		*idx++ = (WORD)( y*x2 + x + 1 );
		*idx++ = (WORD)( y*x2 + x );
		nidx += 3;
	}

	for( x = 0; x < x1; x++ ) {
		*idx++ = wNorthVtx;
		*idx++ = (WORD)( x + 1 );
		*idx++ = (WORD)x;
		nidx += 3;
	}

	//tangents
	mesh.nface = mesh.nidx/3;

	//buffers
	D3D11_BUFFER_DESC bdesc;
	D3D11_SUBRESOURCE_DATA sdata;

	memset( &bdesc, 0, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bdesc.ByteWidth = mesh.nvtx*44;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_IMMUTABLE;

	memset( &sdata, 0, sizeof(sdata) );
	sdata.pSysMem = Vtx;

	HR( gd3dDevice->CreateBuffer( &bdesc, &sdata, &mesh.VB ) );

	memset( &bdesc, 0, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
	bdesc.ByteWidth = mesh.nidx*2;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_IMMUTABLE;

	memset( &sdata, 0, sizeof(sdata) );
	sdata.pSysMem = Idx;

	HR( gd3dDevice->CreateBuffer( &bdesc, &sdata, &mesh.IB ) );
	D3DXComputeBoundingSphere( (D3DXVECTOR3*)&Vtx->PosL, mesh.nvtx, Stride_TILEVERTEX_LOW, &mesh.bsPos, &mesh.bsRad );

	delete [ ] Vtx;
	delete [ ] Idx;
}

void TileRenderMgr::CreateSpherePatch_LOW( TILEMESH1 &mesh, UINT ilat, UINT nlat, UINT nlng, UINT res, UINT bseg, bool reduce, bool shift_origin ) {
	
	//tile angular dimesions
	double	minlat = PI05 * (double)ilat/(double)nlat,
			maxlat = PI05 * (double)(ilat+1)/(double)nlat,
			minlng = 0.0,
			maxlng = PI2/(double)nlng;
	
	//res	vertical
	//bseg	horizontal
	if( bseg < 0 || ilat == nlat-1 )
		bseg = (nlat-ilat)*res;

	mesh.nvtx = (bseg+1)*(res+1);
	if( reduce )
		mesh.nvtx -= ((res+1)*res)/2;

	TILEVERTEX_LOW *vdata = new TILEVERTEX_LOW [mesh.nvtx];

	//vertex buffer
	double lat, slat, clat, lng, slng, clng;
	int i, j, n, nseg;//counters.
	VECTOR3 pos, tpos;

	for( i = n = 0; i <= res; i++ ) {
		lat = minlat + (maxlat-minlat)*(double)i/(double)res;
		slat = sin(lat), clat = cos(lat);
		nseg = ( reduce ? bseg-i : bseg );
		for( j = 0; j <= nseg; j++ ) {
			lng = ( nseg ? minlng + (maxlng-minlng)*(double)j/(double)nseg : 0.0 );
			slng = sin(lng), clng = cos(lng);

			pos = _V( clat*clng, slat, clat*slng );

			vdata[n].PosL = D3DXVECTOR3( (float)pos.x, (float)pos.y, (float)pos.z );
			vdata[n].PosL *= 1000.0f;

			vdata[n].NormL = D3DXVECTOR3( (float)pos.x, (float)pos.y, (float)pos.z );

			vdata[n].TangL = D3DXVECTOR3( -slng, 0.0f, clng );

			vdata[n].TCrd.x = (float)( nseg ? (float)j/(float)nseg : 0.5f );
			vdata[n].TCrd.y = (float)( ((float)res-(float)i)/(float)res );

		//	vdata[n].mtex.x = (nseg ? vdata[n].tex.x * TEX2_MULTIPLIER : 0.5f);
		//	vdata[n].mtex.y = vdata[n].tex.y * TEX2_MULTIPLIER;
			n++;
		}
	}

	//index buffer:
	mesh.nidx = ( reduce ? res*(2*bseg-res) : 2*res*bseg )*3;
	WORD *idata = new WORD [mesh.nidx];
	int nofs0, nofs1;

	for( i = n = nofs0 = 0; i < res; i++ ) {
		nseg = ( reduce ? bseg-i : bseg );
		nofs1 = nofs0 + nseg + 1;
		for( j = 0; j < nseg; j++ ) {
			idata[n++] = nofs0 + j;
			idata[n++] = nofs1 + j;
			idata[n++] = nofs0 + j + 1;
			if( reduce && j == nseg-1 )
				break;
			idata[n++] = nofs0 + j + 1;
			idata[n++] = nofs1 + j;
			idata[n++] = nofs1 + j + 1;
		}
		nofs0 = nofs1;
	}

	//=================================
	D3D11_BUFFER_DESC bdesc;
	D3D11_SUBRESOURCE_DATA sdata;

	memset( &bdesc, 0, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bdesc.ByteWidth = mesh.nvtx*Stride_TILEVERTEX_LOW;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_IMMUTABLE;

	memset( &sdata, 0, sizeof(sdata) );
	sdata.pSysMem = vdata;

	HR( gd3dDevice->CreateBuffer( &bdesc, &sdata, &mesh.VB ) );

	memset( &bdesc, 0, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
	bdesc.ByteWidth = mesh.nidx*2;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_IMMUTABLE;

	memset( &sdata, 0, sizeof(sdata) );
	sdata.pSysMem = idata;

	HR( gd3dDevice->CreateBuffer( &bdesc, &sdata, &mesh.IB ) );

	//compute tangents
	mesh.nface = mesh.nidx/3;

	D3DXComputeBoundingSphere( (D3DXVECTOR3*)vdata, mesh.nvtx, Stride_TILEVERTEX_LOW, &mesh.bsPos, &mesh.bsRad );

	delete [ ] vdata;
	delete [ ] idata;
}

void TileRenderMgr::CreateSpherePatch_HIGH( TILEVERTEX_HIGH *vtx, UINT16 *Idx, UINT ilat, UINT res, UINT bseg ) {

	int reduce = (ilat == 7 ? 1 : 0 );
	double	minlat = PI05 * (double)ilat/8.0,
			maxlat = PI05 * (double)(ilat+1)/8.0,
			minlng = 0.0,
			maxlng = PI2/32.0;

	if( bseg < 0 || reduce )
		bseg = (8-ilat)*res;

	UINT nvtx = (bseg+1)*(res+1);
	if( reduce )	nvtx -= ((res+1)*res)/2;

	TILEVERTEX_HIGH *vdata = vtx;

	//======================================
	//		Vertex buffer
	//======================================
	double lat, slat, clat, lng, slng, clng;
	int i, j, n, nseg;
	VECTOR3 pos, tpos;

	double lat_shift = reduce ? minlat : 0.0;

	double dlat = (maxlat - minlat)/res;
	lat = minlat;
	lng = minlng;

	for( i = n = 0; i <= res; i++ ) {

		if( i == 0 )			lat = minlat;
		else if( i == res )		lat = maxlat;
		else					lat = minlat + (maxlat-minlat)*(double)(i-1)/(double)(res-2+reduce);

		nseg = (reduce ? bseg - i : bseg);
		for( j = 0; j <= nseg; j++ ) {

			if( j == 0 )			lng = minlng;
			else if( j == nseg )	lng = maxlng;
			else					lng = ( nseg ? minlng + (maxlng-minlng)*(double)(j-1)/(double)(nseg-2+reduce) : 0.0 );

			vtx[n].PosL.x = lat - lat_shift;
			vtx[n].PosL.y = lng;
			
			if( j == 0 || j == nseg )	vtx[n].TCrd.x = (j == nseg ? 1.0f : 0.0f);
			else						vtx[n].TCrd.x = lng/(maxlng - minlng);

			if( i == 0 || i == res )	vtx[n].TCrd.y = (i == res ? 0.0f : 1.0f);
			else						vtx[n].TCrd.y = 1.0f - (lat - minlat)/(maxlat-minlat);

			vtx[n].flag = 0;

			if( j == 0 || j == nseg || i == 0 || i == res )		vtx[n].flag = 1;
			n++;
		}
	}

	//======================================
	//		Index buffer
	//======================================
	UINT nidx = ( reduce ? res*(2*bseg-res) : 2*res*bseg )*3;
	WORD *idata = Idx;
	int nofs0, nofs1;

	for( i = n = nofs0 = 0; i < res; i++ ) {
		nseg = ( reduce ? bseg-i : bseg );
		nofs1 = nofs0 + nseg + 1;
		for( j = 0; j < nseg; j++ ) {
			idata[n++] = nofs0 + j;
			idata[n++] = nofs1 + j;
			idata[n++] = nofs0 + j + 1;
			if( reduce && j == nseg-1 )
				break;
			idata[n++] = nofs0 + j + 1;
			idata[n++] = nofs1 + j;
			idata[n++] = nofs1 + j + 1;
		}
		nofs0 = nofs1;
	}
}

TILEVERTEX_LOW _TVTX_LOW( D3DXVECTOR3 pos, D3DXVECTOR3 norm, float tu, float tv ) {
	TILEVERTEX_LOW out;
	out.PosL = pos;
	out.NormL = norm;
	out.TangL = D3DXVECTOR3( -1.0f, -1.0f, -1.0f );
	out.TCrd.x = tu;
	out.TCrd.y = tv;
	return out;
}

void TileRenderMgr::SetWaterMicroTexture( const char *fname ) {
	static bool water_micro_loaded = false;

	if( !fname || water_micro_loaded )	return;

	char cpath[256];
	FILE *file;
	HRESULT hr;
	DWORD key;
	BYTE *data;
	DWORD bufsize;

	if( !gGraphicsClient->TexturePath( fname, cpath ) )	return;
	if( !(file = fopen( cpath, "rb" ) ) )
		return;

	fread( &key, 4, 1, file );
	if( key != MAKEFOURCC( 'D', 'D', 'S', ' ' ) )
		return;

	fseek( file, 0, SEEK_END );
	bufsize = ftell( file );
	fseek( file, 0, SEEK_SET );
	data = new BYTE [bufsize+1];
	fread( data, 1, bufsize, file );
	fclose( file );

	D3DX11_IMAGE_INFO IInfo;
	D3DX11_IMAGE_LOAD_INFO LInfo;

	D3DX11GetImageInfoFromMemory( data, bufsize, NULL, &IInfo, &hr );
	if( hr != S_OK ) {
		delete [ ] data;
		return;
	}

	LInfo.BindFlags = D3D11_BIND_SHADER_RESOURCE;
	LInfo.CpuAccessFlags = 0;
	LInfo.Depth = 1;
	LInfo.Filter = D3DX11_FILTER_NONE;
	LInfo.FirstMipLevel = 0;
	LInfo.Format = IInfo.Format;
	LInfo.MipFilter = D3DX11_FILTER_NONE;
	LInfo.MipLevels = 1;
	LInfo.MiscFlags = 0;
	LInfo.pSrcInfo = &IInfo;
	LInfo.Usage = D3D11_USAGE_IMMUTABLE;
	LInfo.Width = IInfo.Width;
	LInfo.Height = IInfo.Height;

	D3DX11CreateShaderResourceViewFromMemory( gd3dDevice, data, bufsize, &LInfo, NULL, &WaterMicroTex, &hr );
#if _DEBUG
	{	
		char buffer[200];
		buffer[199] = '\0';
		sprintf(buffer, "[TileRenderMgr::SetWaterMicroTexture]%s", fname);
		if (WaterMicroTex)
			WaterMicroTex->SetPrivateData(WKPDID_D3DDebugObjectName, 200, buffer);
	}
#endif
	delete [ ] data;

	water_micro_loaded = true;
}

CComPtrEx<ID3D11VertexShader>
	TileRenderMgr::VS_Shadow;
CComPtrEx<ID3D11GeometryShader>
	TileRenderMgr::GS_Shadow;
CComPtrEx<ID3D11PixelShader>
	TileRenderMgr::PS_Shadow;
CComPtrEx<ID3D11Buffer>
	TileRenderMgr::cb_per_frame,
	TileRenderMgr::cb_per_tile;

void TileRenderMgr::InitTerrainShadows()
{
	HRESULT hr;
	ID3DBlob *SBlob, *EBlob;

	::CompileFromFile( "Modules\\D3D11Shaders\\VS_Planet1_Shadow.fx", nullptr, NULL, "VS_Shadow", vs_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
	ShowShaderCompilationError( hr, EBlob );
	HR( gd3dDevice->CreateVertexShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &VS_Shadow ) );
	REL( SBlob );
	REL( EBlob );

	::CompileFromFile( "Modules\\D3D11Shaders\\GS_Planet1_Shadow.fx", nullptr, NULL, "GS_Shadow", gs_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
	ShowShaderCompilationError( hr, EBlob );
	HR( gd3dDevice->CreateGeometryShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &GS_Shadow ) );
	REL( SBlob );
	REL( EBlob );

/*	::CompileFromFile( "Modules\\D3D11Shaders\\PS_Planet1_Shadow.fx", nullptr, NULL, "PS_Shadow", ps_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
	ShowShaderCompilationError( hr, EBlob );
	HR( gd3dDevice->CreatePixelShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &PS_Shadow ) );
	REL( SBlob );
	REL( EBlob );
*/
	D3D11_BUFFER_DESC bdesc;
	memset( &bdesc, 0, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	bdesc.ByteWidth = 16;
	bdesc.Usage = D3D11_USAGE_DEFAULT;
	gd3dDevice->CreateBuffer( &bdesc, nullptr, &cb_per_frame );

	bdesc.ByteWidth = 112;
	gd3dDevice->CreateBuffer( &bdesc, nullptr, &cb_per_tile );
}

void TileRenderMgr::ExitTerrainShadows()
{
	VS_Shadow.Release();
	GS_Shadow.Release();
//	PS_Shadow.Release();
	cb_per_frame.Release();
	cb_per_tile.Release();
}

void TileRenderMgr::RenderTerrainShadows( std::vector<TILERENDERSPEC> &trs, float dist_scale, float coef_altitude )
{
	UINT j;
	const UINT VBOffset = 0;
	
	CBShadow_per_frame cbpf;
	cbpf.dist_scale = dist_scale;
	cbpf.coef_altitude = coef_altitude;
	iCtx->UpdateSubresource( cb_per_frame, 0, nullptr, &cbpf, 0, 0 );

	iCtx->IASetInputLayout( IL_TILEVERTEX_HIGH );
	iCtx->IASetVertexBuffers( 0, 1, &VB_TPL, &Stride_TILEVERTEX_HIGH, &VBOffset );
	iCtx->IASetIndexBuffer( IB, DXGI_FORMAT_R16_UINT, 0 );
	iCtx->RSSetState( RS_CullNone_Solid );
	
	iCtx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
	iCtx->VSSetShader( VS_Shadow, NULL, 0 );
	iCtx->VSSetSamplers( 0, 1, &SS_HeightMap );

	ID3D11Buffer *cb[2] = { cb_per_frame, cb_per_tile };
	iCtx->VSSetConstantBuffers( 0, 2, cb );

	iCtx->GSSetShader( GS_Shadow, nullptr, 0 );

	iCtx->PSSetShader( nullptr, nullptr, 0 );
	iCtx->OMSetBlendState( BS_NoBlend, D3DXVECTOR4( 1, 1, 1, 1 ), 0xFFFFFFFF );

	CBShadow_per_tile cbuf;
	for( j = 0; j < trs.size(); j++ ) {

		cbuf.mWorld = trs[j].mWorld;
		cbuf.coef_lat_0 = (float)(PI05*((double)trs[j].ilat/(double)trs[j].nlat));
		cbuf.coef_lat_1 = (float)(8.0/(double)trs[j].nlat);
		cbuf.coef_lng = (float)((8.0*4.0*(double)trs[j].nlat)/((double)trs[j].nlng*(double)trs[j].nlat));

		if( trs[j].nlat > 10 ) {
			cbuf.cos_minlat = (float)(1000.0*cos((PI05*(double)trs[j].ilat)/(double)trs[j].nlat));
			cbuf.sin_minlat = (float)(1000.0*sin((PI05*(double)trs[j].ilat)/(double)trs[j].nlat));
		}
		else
			cbuf.cos_minlat = cbuf.sin_minlat = 0.0f;


		iCtx->VSSetShaderResources( 0, 1, trs[j].Tex.htex->GetTexture() );//VS if FL10

		//tex coord range for heightmap
		cbuf.TexOff_height[0] = trs[j].RANGE.range_height.tumax - trs[j].RANGE.range_height.tumin;
		cbuf.TexOff_height[1] = trs[j].RANGE.range_height.tumin;
		cbuf.TexOff_height[2] = trs[j].RANGE.range_height.tvmax - trs[j].RANGE.range_height.tvmin;
		cbuf.TexOff_height[3] = trs[j].RANGE.range_height.tvmin;
		cbuf.ncascade = trs[j].ncascade;

		iCtx->UpdateSubresource( cb_per_tile, 0, nullptr, &cbuf, 0, 0 );
		
	}

	iCtx->GSSetShader( nullptr, nullptr, 0 );
}
